import cv2
import numpy as np


# # 灰度化
#
# if __name__ == '__main__':

def hd(path):
    image_np = cv2.imread(path)
    image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)
    print(image_np_gray)
    return image_np_gray


# 二值化
def ez(path):
    print('''\
                   ************************************************
                   *                 a = 手动阈值法                  *
                   *                 b = 手动反阈值                  *
                   *                 c = otsu阈值                   *
                   *                 d = otsu反阈值                 *
                   ************************************************''')

    method = input('请输入二值化所要使用的阈值法')
    if method == 'a' or 'b':
        thresh = int(input('请输入阈值'))

    elif method == 'c' or 'd':
        thresh = 127
    else:
        print('输入错误')

    maxval = 255
    image_np_gray = hd(path)
    if method == 'a' and 0 <= thresh <= 255 and 0 <= maxval <= 255:
        ret, image_thresh = cv2.threshold(image_np_gray, thresh, maxval, cv2.THRESH_BINARY)

    elif method == 'b' and 0 <= thresh <= 255 and 0 <= maxval <= 255:
        ret, image_thresh = cv2.threshold(image_np_gray, thresh, maxval, cv2.THRESH_BINARY_INV)

    elif method == 'c' and 0 <= thresh <= 255 and 0 <= maxval <= 255:
        ret, image_thresh = cv2.threshold(image_np_gray, thresh, maxval, cv2.THRESH_OTSU + cv2.THRESH_BINARY)

    elif method == 'd' and 0 <= thresh <= 255 and 0 <= maxval <= 255:
        ret, image_thresh = cv2.threshold(image_np_gray, thresh, maxval, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)

    else:
        print("输入错误，请重新输入")
    return image_thresh


# 形态学变换
def xb(path):
    print('''\
                           ************************************************
                           *                 a =  矩形                  *
                           *                 b =  椭圆形                *
                           *                 c =  十字形                *
                           *                 e =  腐蚀                  *
                           *                 d =  膨胀                  *
                           *                 o =  开运算                *
                           ************************************************''')

    ker_morph, ker_method = input('请依次输入核形状及形变类型').split()
    ker_size = int(input('请输入核大小'))
    maxval = 255
    image_thresh = ez(path)
    if ker_morph == 'a':
        kernel = cv2.getStructuringElement(
            cv2.MORPH_RECT,  # 核的形状
            (ker_size, ker_size)  # 核的尺寸
        )

    elif ker_morph == 'b':
        kernel = cv2.getStructuringElement(
            cv2.MORPH_ELLIPSE,  # 核的形状
            (ker_size, ker_size)  # 核的尺寸
        )

    elif ker_morph == 'c':
        kernel = cv2.getStructuringElement(
            cv2.MORPH_CROSS,  # 核的形状
            (ker_size, ker_size)  # 核的尺寸
        )
    else:
        print("输入错误，请重新输入")
    if ker_method == 'e':
        morph_image = cv2.erode(
            image_thresh,  # 二值化图像
            kernel  # 核
        )

    elif ker_method == 'd':
        morph_image = cv2.dilate(
            image_thresh,  # 二值化图像
            kernel  # 核
        )

    elif ker_method == 'o':
        morph_image = cv2.erode(
            image_thresh,  # 二值化图像
            kernel  # 核
        )

        morph_image = cv2.dilate(
            morph_image,  # 二值化图像
            kernel  # 核
        )

    else:
        print("输入错误，请重新输入")

    return morph_image


# 颜色识别替换
def sbth(path):
    image_np = cv2.imread(path)
    image_np_hsv = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV)

    n, open, change = map(int, input('请依次输入颜色种数、是否开运算（0 or 1）、是否进行颜色替换（0 or 1）').split())

    # 制作掩膜
    mask = 0
    for i in range(n):
        low = np.array(list(map(int, input("请输入色值低三位h、s、v").split())))
        high = np.array(list(map(int, input("请输入色值高三位h、s、v").split())))
        print(low)
        if [0, 0, 0] <= [low[0], low[1], low[2]] <= [255, 255, 255] and [0, 0, 0] <= [high[0], high[1], high[2]] <= [255, 255, 255]:
            mask1 = cv2.inRange(image_np_hsv, low, high)
            mask = cv2.bitwise_or(mask, mask1)
        else:
            print("输入错误，请重新输入")
    if open == 1:
        ker = int(input('请输入核的大小'))
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (ker, ker))
        mask = cv2.morphologyEx(src=mask, op=cv2.MORPH_OPEN, kernel=kernel)
    color_image_np = cv2.bitwise_and(image_np, image_np, mask=mask)
    if change == 0:
        return color_image_np
    elif change == 1:
        h, s, v = map(int, input('请输入你需要的颜色色值h、s、v').split())
        image_np_color_hsv = np.where(color_image_np != 0, (h, s, v), image_np_hsv)  # 待测
        image_np_color_hsv = image_np_color_hsv.astype(np.uint8)
        print(image_np_color_hsv)
        image_np_color = cv2.cvtColor(image_np_color_hsv, cv2.COLOR_HSV2BGR)
        return image_np_color


# 画线
def map_(path):
    cho = input('请输入是否要画线True or False')
    if cho == 'True':
        image_np = cv2.imread(path)
        h = image_np.shape[0]
        w = image_np.shape[1]
        print(f'图像范围为{w, h}')

        x_min, x_max = map(int, input('请输入横轴范围').split())
        y_min, y_max = map(int, input('请输入纵轴范围').split())
        try:
            if not (x_min >= 0 and y_min >= 0 and x_max < w and y_max < h and x_max > x_min and y_max > y_min):
                raise ValueError('ROI区域设定有误')
            line_width = 2
            cv2.rectangle(image_np, (x_min, y_min), (x_max, y_max), (0, 0, 255), line_width)
            cv2.imshow('', image_np)
            cv2.imwrite('map.jpg', image_np)
            cv2.waitKey(0)
            return x_min, x_max, y_min, y_max
        except Exception as e:
            print('程序运行出错', e)


# roi切割
def roi(path):
    x_min, x_max, y_min, y_max = map_(path)
    image_np = cv2.imread(path)
    ROI_image = image_np.copy()[y_min:y_max, x_min:x_max]
    return ROI_image


# 插值
def chazhi():
    print('''\
            ************************************************
            *                 a = 最近邻插值                  *
            *                 b = 双线性插值                  *
            *                 c = 像素区域插值                *
            *                 d = 双三次插值                  *
            *                 e = Lanczos插值                *

            ************************************************''')

    inter = input('请输入插值方法')
    if inter == 'a':
        interpolation = cv2.INTER_NEAREST

    if inter == 'b':
        interpolation = cv2.INTER_LINEAR

    if inter == 'c':
        interpolation = cv2.INTER_AREA

    if inter == 'd':
        interpolation = cv2.INTER_CUBIC

    if inter == 'e':
        interpolation = cv2.INTER_LANCZOS4

    return interpolation


# 边缘填充
def tinachong():
    print('''\
                    ************************************************
                    *                 a = 边界复制                  *
                    *                 b = 边界反射                  *
                    *                 c = 边界反射101               *
                    *                 d = 边界常数                  *
                    *                 e = 边界包裹                  *

                    ************************************************''')

    border = input('请输入边缘填充方法')
    if border == 'a':
        borderMode = cv2.BORDER_REPLICATE

    if border == 'b':
        borderMode = cv2.BORDER_REFLECT

    if border == 'c':
        borderMode = cv2.BORDER_REFLECT_101

    if border == 'd':
        borderMode = cv2.BORDER_CONSTANT

    if border == 'e':
        borderMode = cv2.BORDER_WRAP

    return borderMode


# 图像旋转
def xz(path):
    cho = input('请选择旋转方式(a普通旋转 or b镜像旋转)')
    if cho == 'a':
        map_(path)
        image_np = cv2.imread(path)
        x, y = map(int, input('请输入旋转点').split())
        angle, scale = map(float, input('请输入旋转角度与缩放倍率').split())
        M = cv2.getRotationMatrix2D((x, y), angle, scale)
        flags = chazhi()
        borderMode = tinachong()
        rotation_image = cv2.warpAffine(image_np, M, (image_np.shape[1], image_np.shape[0]), flags=cv2.INTER_LANCZOS4,
                                        borderMode=cv2.BORDER_WRAP)
    elif cho == 'b':
        image_np = cv2.imread(path)
        rotation_image = cv2.flip(
            image_np,  # 要反转的图像
            -1,  # 翻转码
        )
    return rotation_image


# 图像缩放
def sf(path):
    image_np = cv2.imread(path)
    x, y = map(float, input('请输入x、y的缩放倍率').split())

    resize_image = cv2.resize(
        image_np,  # 原图
        (0, 0),  # 目标分辨率(宽高)，如果设置这个参数，x轴和y轴的缩放比例失效
        fx=x,  # x轴倍率
        fy=y,  # y轴倍率
        interpolation=cv2.INTER_LINEAR  # 插值方法
    )

    return resize_image


# 图像矫正
def jz(path):
    image_np = cv2.imread(path)
    points = list(map(int, input('请输入原图左上、右上、左下、右下四角坐标').split()))

    points1 = [
        [points[0], points[1]],  # 左上
        [points[2],points[3]],  # 右上
        [points[4], points[5]],  # 左下
        [points[6], points[7]]  # 右下
    ]
    pts1 = np.float32(points1)

    print(points)
    image_line = image_np.copy()
    # 在拷贝的原图上标记ROI区域
    cv2.line(
        image_line,  # 在哪个图像上画线
        [points[0], points[1]],  # 起点
        [points[2],points[3]],  # 终点
        (0, 0, 255),  # 颜色
        2,  # 粗度
        cv2.LINE_AA  # 抗锯齿
    )
    cv2.line(
        image_line,  # 在哪个图像上画线
        [points[2], points[3]],  # 起点
        [points[6], points[7]],  # 终点
        (0, 0, 255),  # 颜色
        2,  # 粗度
        cv2.LINE_AA  # 抗锯齿
    )
    cv2.line(
        image_line,  # 在哪个图像上画线
        [points[6], points[7]],  # 起点
        [points[4], points[5]],  # 终点
        (0, 0, 255),  # 颜色
        2,  # 粗度
        cv2.LINE_AA  # 抗锯齿
    )
    cv2.line(
        image_line,  # 在哪个图像上画线
        [points[4], points[5]],  # 起点
        [points[0], points[1]],  # 终点
        (0, 0, 255),  # 颜色
        2,  # 粗度
        cv2.LINE_AA  # 抗锯齿
    )
    cv2.imshow('原图标记', image_line)
    cv2.waitKey(0)
    # 3. 获取透视变换矩阵
    img_shape = image_np.shape
    # 计算目标图像dst的四个角点
    points = [
        [0, 0],  # 左上
        [img_shape[1], 0],  # 右上
        [0, img_shape[0]],  # 左下
        [img_shape[1], img_shape[0]]
    ]
    # list → nd数组
    pts2 = np.float32(points)

    # 构建透视变换矩阵M
    M = cv2.getPerspectiveTransform(
        pts1,  # src原始图像的四个卡片角点
        pts2,  # dst目标图像的四个卡片角点
    )
    # 4. 透视变换
    flags = chazhi()
    borderMode = tinachong()
    correct_image = cv2.warpPerspective(
        image_np,  # 要处理的图像
        M,  # 透视变换矩阵
        (img_shape[1], img_shape[0]),  # 目标图像的分辨率
        flags,  # 5. 插值方法
        borderMode  # 6. 边缘填充方法
    )

    return correct_image


# 水印添加
def shuiyin(path, path2):
    # path2 = input('请输入模板')
    # path2 = input('请输入模板')
    image_np = cv2.imread(path)


    logo = cv2.imread(path2)

    mask_logo = ez(path2)

    rows, cols = logo.shape[:2]
    # 在lena图中ROI一个上面尺寸的区域
    x, y = map(int, input('请输入Logo起点坐标').split())
    roi = image_np[y:y + rows, x:x + cols]
    # 按位与
    image_np_bg = cv2.bitwise_and(
        roi,
        roi,
        mask=mask_logo
    )

    # 6. 图像融合
    dst = cv2.add(image_np_bg, logo)
    # dst是小图融合后的效果，需要把像素值替换到原始lena图中
    image_np[y:y+ rows, x:x + cols] = dst
    return image_np


# 噪点消除
def zdxc(path):
    image_np = cv2.imread(path)
    print('''\
                           ************************************************
                           *                 a = 均值滤波                  *
                           *                 b = 方框滤波                  *
                           *                 c = 高斯滤波                  *
                           *                 d = 中值滤波                  *
                           *                 e = 双边滤波                  *

                           ************************************************''')

    cho = input('请输入滤波方法')
    ksize = int(input('请输入核的大小'))
    if cho == 'a':
        no_noise_image = cv2.boxFilter(
            image_np,  # 原图
            (ksize, ksize)
        )

    elif cho == 'b':
        no_noise_image = cv2.boxFilter(
            image_np,  # 原图
            -1,  # 位深度
            (ksize, ksize),  # 核
            normalize=False  # 标准化参数
        )

    elif cho == 'c':
        SigmaX = float(input('请输入SmgmaX(例：1)'))
        no_noise_image = cv2.GaussianBlur(
            image_np,  # 要滤波的图像
            (ksize, ksize),  # 核
            SigmaX  # SigmaX
        )
    elif cho == 'd':
        no_noise_image = cv2.medianBlur(
            image_np,  # 要滤波的图像
            ksize  # 核
        )

    elif cho == 'e':
        sigma1 = float(input('请输入sigmaColor'))
        sigma2 = float(input('请输入sigmaSpace'))
        no_noise_image = cv2.bilateralFilter(
            image_np,  # 要滤波的图像
            ksize,  # 核
            sigma1,  # SigmaColor
            sigma2  # SigmaSpace
        )

    else:
        print('输入错误请重新输入')

    return no_noise_image


# 对比度增强
def duibi(path):
    image_np = cv2.imread(path)

    print('''\
                                   ************************************************
                                   *                 a = 直方图均衡化                 *
                                   *                 b = 自适应均衡化                 *
                                   ************************************************''')

    cho = input('请输入滤波方法')
    image_np_gray = hd(path)
    if cho == 'a':
        equ_hist_image_np = cv2.equalizeHist(image_np_gray)

    elif cho == 'b':

        a, b = map(int, input('请输入对比度限制及分块大小（例：3 80）').split())
        clahe = cv2.createCLAHE(
            clipLimit=a,
            tileGridSize=(b, b)
        )
        equ_hist_image_np = clahe.apply(image_np_gray)

    else:
        print('输入错误请重新输入')

    return equ_hist_image_np


# Canny算法
def bianyuan(path):
    image_np_thresh = ez(path)
    a, b = map(int, input('请输入canny低阈值 高阈值(例：20 40)').split())
    edges_image = cv2.Canny(
        image_np_thresh,  # 要处理的图像
        a,  # 低阈值
        b  # 高阈值
    )

    return edges_image


# 轮廓
def lunkuo(path):
    image_np = cv2.imread(path)
    image_np_thresh = ez(path)
    conunters, hierarchy = cv2.findContours(image_np_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    for cnt in conunters:
        print(cnt.shape)

    a, b = map(int, input('请根据输出的边界值， 输入所要画的轮廓点的范围').split())
    for cnt in conunters:
        if a < cnt.shape[0] < b:
            # x, y, z = map(int, np.random.randint(0, 256, 3))
            image_np = cv2.drawContours(image_np, cnt, contourIdx=-1, color=(0, 255, 0), thickness=2)

    return image_np


# 模块匹配
def mkpp(path):
    image_np = cv2.imread(path)
    path2 = input('请输入要匹配的模块')
    template = cv2.imread(path2)
    h = template.shape[0]
    w = template.shape[1]
    print(h, w)
    res = cv2.matchTemplate(image_np, template, cv2.TM_CCOEFF_NORMED)
    print(res.max())

    threshold = float(input('请输入匹配阈值(li：0.89)'))

    loc = np.where(res > threshold)
    print(len(loc))
    print(res.shape)
    print(loc)
    for x, y in zip(loc[1], loc[0]):
        print('坐标：', (x, y))
        # 画框
        cv2.rectangle(
            image_np,
            (x, y),
            (x + w, y + h),  # 右下角是左上角加上模板的宽高
            (0, 0, 255),
            thickness=2
        )

        # print(loc[0][i])

    return image_np


# 寻找直线
def zx(path):
    image_edges = bianyuan(path)
    image_np = cv2.imread(path)
    print('''\
                     ************************************************
                     lines = cv2.HoughLinesP(
                     image_edges,  # 边缘检测图像
                     0.8,  # ρ的密度
                     np.pi / 180,  # θ的密度
                     90,  # 投票器累加阈值
                     minLineLength=50,  # 最短直线长度
                     maxLineGap=10  # 线距
    )

                     ************************************************''')

    a, b, c, d, e = map(float, input('请依次输入ρ密度、θ密度、投票累加器阈值、最短直线长度、线距').split())
    lines = cv2.HoughLinesP(
        image_edges,  # 边缘检测图像
        a,  # ρ的密度
        np.pi / b,  # θ的密度
        int(c),  # 投票器累加阈值
        minLineLength=d,  # 最短直线长度
        maxLineGap=e  # 线距
    )

    for line in lines:
        print(line[0])
        x1, y1, x2, y2 = line[0]
        # 画线
        cv2.line(
            image_np,
            (x1, y1),
            (x2, y2),
            (0, 0, 255),
            1,
            lineType=cv2.LINE_AA
        )

    return image_np


# 圆
def yuan(path):
    image_edges = bianyuan(path)
    image_np = cv2.imread(path)

    a, b, c = map(float, input('请依次输入dp密度、最小圆心距离、累加器投票阈值').split())
    circles = cv2.HoughCircles(
        image_edges,  # 边缘检测图像
        cv2.HOUGH_GRADIENT,  # 霍夫梯度法
        a,  # dp
        b,  # 最小圆心距离
        param2=c  # 累加器投票阈值
    )
    print(circles)  # 圆数据

    # 5. 图片输出

    for circle in circles[0]:
        print(circle)
        x, y, r = circle
        # 画圆
        cv2.circle(
            image_np,
            (int(x), int(y)),
            int(r),
            (0, 0, 255),
            2,
            lineType=cv2.LINE_AA
        )
        # 圆心
        cv2.circle(
            image_np,
            (int(x), int(y)),
            1,
            (0, 0, 255),
            2,
            lineType=cv2.LINE_AA
        )
    return image_np


# 亮度
def ld(path):
    image_np = cv2.imread(path)

    beta = int(input('请输入要变化的亮度'))  # 控制亮暗
    alpha = 1  # 控制对比度
    pix_image_np = np.clip(
        alpha * image_np + beta,  # 要进行的计算
        0,  # 下限
        255  # 上限
    )
    # 转换无符号8位整型
    pix_image_np = np.uint8(pix_image_np)
    return pix_image_np
